package org.wabson.alfresco.translations;

import java.io.IOException;
import java.text.NumberFormat;
import java.util.Properties;
import java.util.logging.Logger;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang3.StringEscapeUtils;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import org.wabson.alfresco.translations.bean.JarListLoader;
import org.wabson.alfresco.translations.bean.Jarfile;

public class BundleCompareServlet extends HttpServlet
{
    public static final String BLOB_KEY_PARAM_NAME = "k";
    public static final String MESSAGE_BUNDLE_SUFFIX_PARAM_NAME = "s";
    public static final String COMPARE_VERSION_PARAM_NAME = "v";
    public static final String DEFAULT_COMPARE_VERISON = "community-3.3";
    public static final String COMPARE_PACKAGE_PARAM_NAME = "p";
    
    private static Logger logger = Logger.getLogger(BundleCompareServlet.class.getName());

    /**
     * 
     */
    private static final long serialVersionUID = -8257907161559122261L;

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException
    {
    	WebApplicationContext springContext = WebApplicationContextUtils
    			.getRequiredWebApplicationContext(getServletContext());
    	
        StringBuffer sb = new StringBuffer();

        String blobKeyName = req.getParameter(BLOB_KEY_PARAM_NAME);
        String suffix = req.getParameter(MESSAGE_BUNDLE_SUFFIX_PARAM_NAME);
        String release = req.getParameter(COMPARE_VERSION_PARAM_NAME); // e.g. 'community-3.2r', 'enterprise-3.2.0r'
        String packageName = req.getParameter(COMPARE_PACKAGE_PARAM_NAME);
        
        resp.setContentType("text/html; charset=UTF-8");
        resp.setCharacterEncoding("UTF-8");

        if (blobKeyName == null || blobKeyName.equals(""))
        {
            throw new ServletException("No blob key was specified");
        }
        
        if (release == null)
        {
            release = DEFAULT_COMPARE_VERISON;
        }
        if (packageName != null && !packageName.equals(""))
        {
            packageName = "-" + packageName;
        }
        String[] rcmpts = release.split("-");
        if (rcmpts.length != 2)
        {
            throw new ServletException("Bad release " + release);
        }
        String jarName = "alfresco-" + rcmpts[0] + packageName + "-messages-" + rcmpts[1] + ".jar";
        
        // Fetch the file name or the uploaded JAR/AMP
        String fileName = "";
    	Jarfile jar = ((JarListLoader) springContext.getBean("jar-list-loader")).getJar(blobKeyName);
        
        if (jar == null)
        {
            throw new ServletException("Could not find information on blob " + 
                    blobKeyName + ". Does it exist?");
        }
        else
        {
            fileName = jar.getName();
        }
        
        // Translation package messages base path
        String tMessagesPath = (fileName.endsWith(".amp")) ? "config" :"";
        
        // Base package messages base path
        String bMessagesPath = "";

        try
        {
            JarBundleLoader l1 = ((BundleLoaderFactory) springContext.getBean("bundle-loader-factory")).createLoader(jarName, bMessagesPath);
            JarBundleLoader l2 = ((BundleLoaderFactory) springContext.getBean("langpack-loader-factory")).createLoader(blobKeyName, tMessagesPath);
    
            if (suffix == null || suffix.equals(""))
            {
                // Attempt to guess suffix from file name
                if (fileName.matches("^[-\\w\\._]+[-_][a-z]{2}_[A-Z]{2}\\.[\\w]+$"))
                {
                    suffix = fileName.substring(fileName.lastIndexOf('.') - 5, fileName.lastIndexOf('.'));
                }
                else if (fileName.matches("^[-\\w\\._]+[-_][a-z]{2}\\.[\\w]+$"))
                {
                    suffix = fileName.substring(fileName.lastIndexOf('.') - 2, fileName.lastIndexOf('.'));
                }
            }
            if (suffix != null && !suffix.equals("") && !suffix.startsWith("_"))
            {
                suffix = "_" + suffix;
            }
            
            String [] ignoreBundles = 
                { "site-webscripts/org/alfresco/components/footer/footer.get.properties" };
            
            BundleGroupComparator gc = new BundleGroupComparator(l2, l1);
            gc.setBaseSuffix(suffix);
            BundleGroupComparisonResult gcr = gc.compare(null, ignoreBundles, false);
            NumberFormat nf = NumberFormat.getNumberInstance();
            
            sb.append("<html>\r\n");
            sb.append("<head>\r\n");
            sb.append("<title>Score Translation " + l2.getFileName() + "</title>\r\n");
            sb.append("<link rel=\"stylesheet\" type=\"text/css\" href=\"/assets/screen.css\" />");
            sb.append("<script type=\"text/javascript\" src=\"/assets/utils.js\"></script>");
            sb.append("</head>\r\n");
            sb.append("<body>\r\n");
            sb.append("<div class=\"header\">\r\n");
            sb.append("<p class=\"title\"><a href=\"/\">Alfresco I18N Tools</a></p>\r\n");
            sb.append("</div>\r\n");
            sb.append("<div class=\"body\">\r\n");
            sb.append("<h1>Score Translation " + l2.getFileName() + "</h1>\r\n");
            
            sb.append("<h2 class=\"summary\">Summary</h2>\r\n");

            sb.append("<p>Comparing Translation <a href=\"/details?k=" + blobKeyName + "\">" + l2.getFileName() + "</a> ");
            sb.append("with Base Package <a href=\"/details?j=" + jarName + "\">" + jarName + "</a></p>\r\n");
            
            sb.append("<div class=\"results\">");
            if (gcr.getMessageCoverage() == 1F)
            {
                sb.append("<p>Congratulations! Your translation is complete.</p>\r\n");
            }
            else if (gcr.getMessageCoverage() == 0F)
            {
                sb.append("<p>No translation bundles were found. Are you sure your package was in JAR or AMP format, and your message bundle suffix (" + suffix + ") is correct?</p>\r\n");
            }
            else 
            {
                sb.append("<p>It looks like you still have some work to do.</p>\r\n");
            }
            sb.append("<div class=\"messages-result\"><h3>Message Coverage</h3><p class=\"messages-bar\"><span class=\"outer\"><span class=\"inner\" style=\"width: " + (int) (gcr.getMessageCoverage() * 100F) + "%\"> </span></span> <span>" + (int) (gcr.getMessageCoverage() * 100F) + "%</span> <span class=\"detail\">(" + nf.format(gcr.getNumMessages()) + "/" + nf.format(gcr.getNumMessages() + gcr.getNumMissingMessages()) + ")</span></p>");
            sb.append("<p>\r\n");
            sb.append("<a href=\"#missing-messages\">" + nf.format(gcr.getNumMissingMessages()) + " missing messages</a>\r\n");
            sb.append("<br /><a href=\"#extra-messages\">" + nf.format(gcr.getNumExtraMessages()) + " extra messages</a>\r\n");
            sb.append("</p>\r\n");
            sb.append("</div>");
            sb.append("<div class=\"files-result\"><h3>File Coverage</h3><p class=\"files-bar\"><span class=\"outer\"><span class=\"inner\" style=\"width: " + (int) (gcr.getBundleCoverage() * 100F) + "%\"> </span></span> <span>" + (int) (gcr.getBundleCoverage() * 100F) + "%</span> <span class=\"detail\">(" + nf.format(gcr.getNumBundles()) + "/" + nf.format(gcr.getNumBundles() + gcr.getNumMissingBundles()) + ")</span></p>");
            sb.append("<p>\r\n");
            sb.append("<a href=\"#missing-files\">" + nf.format(gcr.getNumMissingBundles()) + " missing files</a>\r\n");
            sb.append("<br /><a href=\"#extra-files\">" + nf.format(gcr.getNumExtraBundles()) + " extra files</a>\r\n");
            sb.append("</p>\r\n");
            sb.append("</div>");
            sb.append("</div>");
            
            int i = 0;

        	logger.fine("Processing missing messages");
        	
            sb.append("<a name=\"missing-messages\"></a><h2>Missing Messages <span>(" + nf.format(gcr.getNumMissingMessages()) + ")</span></h2>\r\n");
            for (String bundle : gcr.getBundlesWithMissingMessages())
            {
            	logger.fine("Processing missing messages in '" + bundle + "'");
            	
                BundleComparisonResult bundleResult = gcr.getComparisonResult(bundle);

                sb.append("<h3>" + bundle);
                if (bundle.endsWith(".properties"))
                    sb.append(" <span>(" + nf.format(gcr.getNumMissingMessages(bundle)) + "/" + nf.format(gcr.getNumMessages(bundle) + gcr.getNumMissingMessages(bundle) - gcr.getNumExtraMessages(bundle)) + ")</span>");
                sb.append("</h3>\n");

                if (bundleResult != null && !bundleResult.isEmpty()) // Some messages present
                {
                	MessageBundle cb = bundleResult.getCompareToBundle();
                	MessageBundle bb = bundleResult.getBaseBundle();
                	
                    sb.append("<div id=\"" + i + "-results\" style=\"display: none;\" class=\"comparison-result\">\n");
                    
                    if (cb != null)
                    {
                        Properties bp = bb.getProperties();
                        boolean isContinuation = false, containsKey = false;
                        
                        sb.append("<pre class=\"properties\">\n");
                        
                        for (String line : cb.getLines())
                        {
                        	String s = "";
                        	line = line.trim();
                        	if (line.length() > 0)
                        	{
                        		if (!isContinuation)
                        		{
    	                        	if (line.startsWith("#"))
    	                        	{
    	                        		s = "<span class=\"comment\">" + htmlChars(line) + "</span>";
    	                        	}
    	                        	else if (line.indexOf('=') > 0)
    	                        	{
    	                        		String[] parts = line.split("=", 2);
    	                        		String k = parts[0].trim(), v = parts[1].trim();
    	                        		containsKey = bp.containsKey(k);
    	                        		s = "<span class=\"key\">" + htmlChars(k) + "</span><span class=\"sep\">=</span><span class=\"val\">" + htmlChars(v) + "</span>";
    	                        		if (!containsKey)
    	                        		{
    	                        			// Wrap string in a warning span if key not found
    	                            		s = "<span class=\"missing-message\">" + s + "</span>";
    	                        		}
    	                        	}
    	                        	else if (line.indexOf(' ') > 0) // Also recognise spaces are key/val separators
    	                        	{
    	                        		String[] parts = line.split(" ", 2);
    	                        		String k = parts[0].trim(), v = parts[1].trim();
    	                        		containsKey = bp.containsKey(k);
    	                        		s = "<span class=\"key\">" + htmlChars(k) + "</span><span class=\"sep\">=</span><span class=\"val\">" + htmlChars(v) + "</span>";
    	                        		if (!bp.containsKey(k))
    	                        		{
    	                        			// Wrap string in a warning span if key not found
    	                            		s = "<span class=\"missing-message\">" + s + "</span>";
    	                        		}
    	                        	}
    	                        	else
                            		{
                            			s = htmlChars(line);
                            			if (!containsKey)
    	                        		{
    	                        			// Wrap string in a warning span if key not found
    	                            		s = "<span class=\"missing-message\">" + s + "</span>";
    	                        		}
                            		}
                        		}
                        		else
                        		{
	                        		s = "<span class=\"val\">" + htmlChars(line) + "</span>";
                        		}
	                        	isContinuation = (line.endsWith("\\"));
                        	}
                            sb.append(s + "\r\n");
                        }
                        sb.append("</pre>\n");
                    }
                    
                    sb.append("<p class=\"links\">View full <a href=\"/bundle?j=" + jarName + "&f=" + bMessagesPath + bundleResult.getCompareToFilePath() + "\">Base Bundle</a> | \n");
                    sb.append("<a href=\"/bundle?k=" + blobKeyName + "&f=" + tMessagesPath + bundleResult.getBaseFilePath() + "\">Translation Bundle</a></p>\n");
                    sb.append("</div>\n");
                }
                else
                {
                    MessageBundle cb = gcr.getComparisonBundle(bundle);
                    
                    sb.append("<div id=\"" + i + "-results\" style=\"display: none;\" class=\"comparison-result\">\n");
                    if (cb != null)
                    {
                        boolean isContinuation = false;
                        
                        sb.append("<pre class=\"properties\">\n");
                        for (String line : cb.getLines())
                        {
                        	String s = "";
                        	line = line.trim();
                        	if (line.length() > 0)
                        	{
                        		if (!isContinuation)
                        		{
                            		if (line.startsWith("#"))
    	                        	{
    	                        		s = "<span class=\"comment\">" + htmlChars(line) + "</span>";
    	                        	}
                            		else if (line.indexOf('=') > 0)
    	                        	{
    	                        		String[] parts = line.split("=", 2);
    	                        		String k = parts[0].trim(), v = parts[1].trim();
    	                        		s = "<span class=\"missing-message\"><span class=\"key\">" + htmlChars(k) + "</span><span class=\"sep\">=</span><span class=\"val\">" + htmlChars(v) + "</span></span>";
    	                        	}
    	                        	else if (line.indexOf(' ') > 0) // Also recognise spaces are key/val separators
    	                        	{
    	                        		String[] parts = line.split(" ", 2);
    	                        		String k = parts[0].trim(), v = parts[1].trim();
    	                        		s = "<span class=\"missing-message\"><span class=\"key\">" + htmlChars(k) + "</span><span class=\"sep\">=</span><span class=\"val\">" + htmlChars(v) + "</span></span>";
    	                        	}
                        		}
                        		else
                        		{
	                        		s = "<span class=\"missing-message\"><span class=\"val\">" + htmlChars(line) + "</span></span>";
                        		}
                        	}
                            sb.append(s + "\r\n");
                        	isContinuation = (line.endsWith("\\"));
                        }
                        sb.append("</pre>\n");
                    }
                    sb.append("<p class=\"links\">View full <a href=\"/bundle?j=" + jarName + "&f=" + bMessagesPath + bundle.replaceFirst(suffix, "") + "\">Base Bundle</a></p>\n");
                    sb.append("</div>\n");
                }
                
                sb.append("<p class=\"links\">\n");
                sb.append("<a href=\"#\" id=\"" + i + "-showLink\" onclick=\"showResults('" + i + "'); return false;\" style=\"display: inline;\">Show</a>\n");
                sb.append("<a href=\"#\" id=\"" + i + "-hideLink\" onclick=\"hideResults('" + i + "'); return false;\" style=\"display: none;\">Hide</a>\n");
                sb.append("</p>\n");
                
                i ++;
            }
            
            // Extra messages

            sb.append("<a name=\"extra-messages\"></a><h2>Extra Messages <span>(" + nf.format(gcr.getNumExtraMessages()) + ")</span></h2>\r\n");
            for (String bundle : gcr.getBundlesWithExtraMessages())
            {
                BundleComparisonResult bundleResult = gcr.getComparisonResult(bundle);

                sb.append("<h3>" + bundle);
                if (bundle.endsWith(".properties"))
                    sb.append(" <span>(" + nf.format(gcr.getNumExtraMessages(bundle)) + "/" + nf.format(gcr.getNumMessages(bundle) + gcr.getNumExtraMessages(bundle) - gcr.getNumMissingMessages(bundle)) + ")</span>");
                sb.append("</h3>\n");
                
                if (bundleResult != null && !bundleResult.isEmpty())
                {
                	MessageBundle cb = bundleResult.getCompareToBundle();
                	MessageBundle bb = bundleResult.getBaseBundle();
                	
                    sb.append("<div id=\"" + i + "-results\" style=\"display: none;\" class=\"comparison-result\">\n");
                    
                    if (bb != null)
                    {
                        Properties cp = cb.getProperties();
                        boolean isContinuation = false, containsKey = false;
                        
                        sb.append("<pre class=\"properties\">\n");
                        
                        for (String line : bb.getLines())
                        {
                        	String s = "";
                        	line = line.trim();
                        	if (line.length() > 0)
                        	{
                        		if (!isContinuation)
                        		{
                            		if (line.startsWith("#"))
    	                        	{
    	                        		s = "<span class=\"comment\">" + htmlChars(line) + "</span>";
    	                        	}
                            		else if (line.indexOf('=') > 0)
    	                        	{
    	                        		String[] parts = line.split("=", 2);
    	                        		String k = parts[0].trim(), v = parts[1].trim();
    	                        		containsKey = cp.containsKey(k);
    	                        		s = "<span class=\"key\">" + htmlChars(k) + "</span><span class=\"sep\">=</span><span class=\"val\">" + htmlChars(v) + "</span>";
    	                        		if (!containsKey)
    	                        		{
    	                        			// Wrap string in a warning span if key not found
    	                            		s = "<span class=\"extra-message\">" + s + "</span>";
    	                        		}
    	                        	}
                            		else if (line.indexOf(' ') > 0)
    	                        	{
    	                        		String[] parts = line.split(" ", 2);
    	                        		String k = parts[0].trim(), v = parts[1].trim();
    	                        		containsKey = cp.containsKey(k);
    	                        		s = "<span class=\"key\">" + htmlChars(k) + "</span><span class=\"sep\">=</span><span class=\"val\">" + htmlChars(v) + "</span>";
    	                        		if (!containsKey)
    	                        		{
    	                        			// Wrap string in a warning span if key not found
    	                            		s = "<span class=\"extra-message\">" + s + "</span>";
    	                        		}
    	                        	}
                        		}
                        		else
                        		{
	                        		s = "<span class=\"val\">" + htmlChars(line) + "</span>";
	                        		if (!containsKey)
	                        		{
	                        			// Wrap string in a warning span if key not found
	                            		s = "<span class=\"extra-message\">" + s + "</span>";
	                        		}
                        		}
                        	}
                            sb.append(s + "\r\n");
                        	isContinuation = (line.endsWith("\\"));
                        }
                        sb.append("</pre>\n");
                    }
                    
                    sb.append("<p class=\"links\">View full <a href=\"/bundle?j=" + jarName + "&f=" + bMessagesPath + bundleResult.getCompareToFilePath() + "\">Base Bundle</a> | \n");
                    sb.append("<a href=\"/bundle?k=" + blobKeyName + "&f=" + tMessagesPath + bundleResult.getBaseFilePath() + "\">Translation Bundle</a></p>\n");
                    sb.append("</div>\n");
                }
                else
                {
                    MessageBundle bb = gcr.getBaseBundle(bundle);
                    
                    sb.append("<div id=\"" + i + "-results\" style=\"display: none;\" class=\"comparison-result\">\n");
                    if (bb != null)
                    {
                        boolean isContinuation = false;
                        
                        sb.append("<pre class=\"properties\">\n");
                        for (String line : bb.getLines())
                        {
                        	String s = "";
                        	line = line.trim();
                        	if (line.length() > 0)
                        	{
                        		if (!isContinuation)
                        		{
                            		if (line.startsWith("#"))
    	                        	{
    	                        		s = "<span class=\"comment\">" + htmlChars(line) + "</span>";
    	                        	}
                            		else if (line.indexOf('=') > 0)
    	                        	{
    	                        		String[] parts = line.split("=", 2);
    	                        		String k = parts[0].trim(), v = parts[1].trim();
    	                        		s = "<span class=\"extra-message\"><span class=\"key\">" + htmlChars(k) + "</span><span class=\"sep\">=</span><span class=\"val\">" + htmlChars(v) + "</span></span>";
    	                        	}
                            		else if (line.indexOf(' ') > 0)
    	                        	{
    	                        		String[] parts = line.split(" ", 2);
    	                        		String k = parts[0].trim(), v = parts[1].trim();
    	                        		s = "<span class=\"extra-message\"><span class=\"key\">" + htmlChars(k) + "</span><span class=\"sep\">=</span><span class=\"val\">" + htmlChars(v) + "</span></span>";
    	                        	}
                        		}
                        		else
                        		{
	                        		s = "<span class=\"extra-message\"><span class=\"val\">" + htmlChars(line) + "</span></span>";
                        		}
                        	}
                            sb.append(s + "\r\n");
                        	isContinuation = (line.endsWith("\\"));
                        }
                        sb.append("</pre>\n");
                    }
                    sb.append("<p class=\"links\">View full <a href=\"/bundle?k=" + blobKeyName + "&f=" + tMessagesPath + bundle + "\">Translation Bundle</a></p>\n");
                    sb.append("</div>\n");
                }
                
                sb.append("<p class=\"links\">\n");
                sb.append("<a href=\"#\" id=\"" + i + "-showLink\" onclick=\"showResults('" + i + "'); return false;\" style=\"display: inline;\">Show</a>\n");
                sb.append("<a href=\"#\" id=\"" + i + "-hideLink\" onclick=\"hideResults('" + i + "'); return false;\" style=\"display: none;\">Hide</a>\n");
                sb.append("</p>\n");
                
                i ++;
            }
            
            // Missing Bundles
            
            sb.append("<a name=\"missing-files\"></a><h2>Missing Files <span>(" + nf.format(gcr.getNumMissingBundles()) + ")</span></h2>\r\n");
        	sb.append("<p>");
            for (String bundle : gcr.getMissingBundles())
            {
            	sb.append("<a href=\"/bundle?j=" + jarName + "&f=" + bMessagesPath + bundle.replaceFirst(suffix, "") + "\">" + bundle + "</a><br/ >");
            }
        	sb.append("</p>");
            
            // Extra Bundles
            
            sb.append("<a name=\"extra-files\"></a><h2>Extra Files <span>(" + nf.format(gcr.getNumExtraBundles()) + ")</span></h2>\r\n");
        	sb.append("<p>");
            for (String bundle : gcr.getExtraBundles())
            {
            	sb.append("<a href=\"/bundle?k=" + blobKeyName + "&f=" + tMessagesPath + bundle + "\">" + bundle + "</a><br/ >");
            }
        	sb.append("</p>");

            sb.append("</div>");
            sb.append("</body>");
            sb.append("</html>");
           
           resp.getWriter().print(StringEscapeUtils.unescapeJava(sb.toString()));
        }
        catch (BundleLoadingException e)
        {
            throw new ServletException("Could not load translation", e);
        }
    }
    
    private static String htmlChars(String input)
    {
        String output = (input != null ? input : "");
        output = output.replaceAll("<", "&lt;");
        output = output.replaceAll("<", "&lt;");
        output = output.replaceAll("\\\"", "&quot;");
        return output;
    }
    
}
